package com.tbit.uqbike.protocol.AnalyzeImpl;

import com.tbit.uqbike.protocol.ABaseHandleObj;
import com.tbit.uqbike.protocol.ATerPkg;
import com.tbit.uqbike.tergateway.data.TerGatewayData;
import com.tbit.uqbike.tergateway.entity.AConnInfo;
import com.tbit.uqbike.tergateway.entity.RemoteControl;
import com.tbit.uqbike.tergateway.tbittextpkg.*;
import com.tbit.uqbike.util.CharsetName;
import com.tbit.uqbike.util.ConstDefine;
import com.tbit.uqbike.util.DateUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.PooledByteBufAllocator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;

/**
 * Created by MyWin on 2018/5/16 0016.
 */
public class TbitTextDeEnCoder extends AProtocol {
    private static Logger logger = LoggerFactory.getLogger(TbitTextDeEnCoder.class);
    public static final String protocolName = "TbitText";

    public static final byte START_CODE = 0x5b;// [
    public static final byte END_CODE = 0x5d;// ]
    public static final int MIN_PKG_SIZE = 10;
    public static final String PKG_SPILT_STR = ",";
    public static final int INDEX_DT = 0;
    public static final int INDEX_VER = 2;
    public static final int INDEX_MNO = 3;
    public static final int INDEX_CMD = 4;

    static {

    }

    @Override
    public List<ATerPkg> analyzeTerPkg(AConnInfo conn, ByteBuf in) {
        List<ATerPkg> list = new LinkedList<>();
        int endInv = -1;
        boolean startFlag = false;
        while (in.readableBytes() >= MIN_PKG_SIZE) {
            // 找头
            if (!startFlag) {
                while (in.readableBytes() > 0) {
                    if (in.getByte(0) == START_CODE) {
                        startFlag = true;
                        break;
                    } else {
                        in.skipBytes(1);
                    }
                }
            }
            // 找不到头，丢弃所有数据
            if (!startFlag) {
                in.skipBytes(in.readableBytes());
                endInv = -1;
            } else {
                // 找尾
                endInv = -1;
                for (int i = 0; i < in.readableBytes(); i++) {
                    if (in.getByte(i) == END_CODE) {
                        endInv = i;
                        break;
                    }
                }
                if (endInv == -1) {
                    // 数据量大，重新找头，数据量不大继续等待后续数据
                    if (in.readableBytes() > 2048) {
                        startFlag = false;
                        in.skipBytes(1);
                        continue;
                    } else {
                        break;
                    }
                } else {
                    // 取报文
                    if (endInv + 1 > 10) {
                        byte[] data = new byte[endInv + 1 - 2];
                        in.getBytes(0 + 1, data);

                        try {
                            String str = new String(data, 0, data.length, CharsetName.US_ASCII);
                            ATerPkg pkg = analyzePkg(str, data);
                            if (null != pkg) {
                                pkg.setConnId(conn.connId);
                                pkg.setProName(getProtocolName());
                                if (pkg.autoRsp()) {
                                    ByteBuf rsp = pkg.getRsp(conn, AAutoProtocol.getProtocol(protocolName));
                                    if (null != rsp && rsp.readableBytes() > 0) {
                                        conn.downMsg(rsp);
                                    }
                                }
                                list.add(pkg);
                            }
                        } catch (UnsupportedEncodingException e) {
                            e.printStackTrace();
                        }
                    }
                    in.skipBytes(endInv + 1);
                    endInv = -1;
                }
            }
        }
        return list;
    }

    private Date getPkgDt(String dt) {
        if (dt == null || dt.isEmpty()) {
            return new Date();
        } else {
            try {
                return DateUtil.ymdHmsDU.parse(dt);
            } catch (Exception e) {
                return new Date();
            }
        }
    }

    private ATerPkg analyzePkg(String str, byte[] bs) {
        logger.debug(String.format("Recv:%s", str));
        // 各种类型的报文判定
        ATerPkg pkg = null;
        if (str == null || str.isEmpty()) {
            return pkg;
        }
        String[] ss = str.split(PKG_SPILT_STR);
        if (ss.length < 5) {
            return pkg;
        }
        try {
            Date pkgDt = getPkgDt(ss[INDEX_DT]);
            switch (ss[INDEX_CMD]) {
                // 常规位置上报
                case "T3":
                    pkg = analyzeTerPos(ss, "S3");
                    break;
                // 断电报警
                case "T4":
                    pkg = analyzeTerPos(ss, "S4", Integer.toString(TextTerDef.ALARM_BATTERY_BREAK));
                    break;
                // 车门开报警
                case "T5":
                    pkg = analyzeTerPos(ss, "S5", Integer.toString(TextTerDef.ALARM_DOOR_OPENED));
                    break;
                // SOS报警
                case "T6":
                    pkg = analyzeTerPos(ss, "S6", Integer.toString(TextTerDef.ALARM_SOS));
                    break;
                // 低电报警(不带位置)
                case "T7":
                    pkg = analyzeTerPos(ss, "S7", Integer.toString(TextTerDef.ALARM_POWER_LACK));
                    break;
                case "T8":
                    pkg = analyzeTerPos(ss, "S8", Integer.toString(TextTerDef.ALARM_CAR_MOVED));
                    break;
                // 普通单次定位
                case "T9":
                    pkg = analyzeTerPos(ss);
                    break;
                // 后台定位应答
                case "T10":
                    pkg = analyzeTerPos(ss);
                    break;
                // 超速报警
                case "T17":
                    pkg = analyzeTerPos(ss, "S17", Integer.toString(TextTerDef.ALARM_SPEEDING));
                    break;
                // 振动告警
                case "T21":
                    pkg = analyzeTerPos(ss, "S21", Integer.toString(TextTerDef.ALARM_SHAKING));
                    break;
                // 设备拆除告警
                case "T27":
                    pkg = analyzeTerPos(ss, "S27", Integer.toString(TextTerDef.ALARM_TEAR_DOWN));
                    break;
                case "T0":
                    pkg = analyzeHeart(ss);
                    break;
                case "T1":
                    pkg = analyzeLogin(ss);
                    break;


                case "T2":
                    pkg = analyzeControlRsp(ss, TextTerDef.CONTROL_SZCS, String.format("%s=%s", ss[5], ss[6]));
                    break;
                // 设防应答
                case "T12":
                    pkg = analyzeControlRsp(ss, TextTerDef.CONTROL_SF, ss[5]);
                    break;

                // 撤防应答
                case "T13":
                    pkg = analyzeControlRsp(ss, TextTerDef.CONTROL_CF, ss[5]);
                    break;

                // 查询参数
                case "T14":
                    pkg = analyzeControlRsp(ss, TextTerDef.CONTROL_CXCS, ss[5]);
                    break;

                // 油门控制
                case "T15":
                    pkg = analyzeControlRsp(ss, TextTerDef.CONTROL_KY_DY_RSP, ss[5]);
                    break;
                // 升级通知
                case "T16":
                    pkg = analyzeControlRsp(ss, TextTerDef.CONTROL_UPDATE_NOTICE, "");
                    break;

                // 后短下发短信的应答
                case "T18":
                    pkg = analyzeControlRsp(ss, TextTerDef.CONTROL_SEND_SMS, ss[5]);
                    break;

                // 终端收到的短信上报,这个报文是很变态
                case "T19": {
                    pkg = analyzeCommPkg(ss, "S19");
                    String content = getStringFromByte(bs, ss[INDEX_VER]);
                    logger.info(String.format("设备[%s]在[%s]收到来自[%s]的短信[%s]", ss[INDEX_MNO], DateUtil.ymdHmsDU.format(pkgDt), ss[5], content));
                    break;
                }
                default:
                    break;
            }
        } catch (Exception e) {
            logger.error("analyzePkg", e);
        }
        if (null != pkg) {
            pkg.mno = ss[INDEX_MNO];
            pkg.signPkg = bs;
        }
        return pkg;
    }

    private static int getIndex(byte[] data, byte c, int n) {
        int iPos = 0;
        for (int i = 0; i < data.length; i++) {
            if (data[i] == c) {
                if (iPos++ == n) {
                    return i;
                }
            }
        }
        return -1;
    }

    private static boolean isAscii(byte[] data) {
        //循环所有字符
        for (byte c : data) {
            if (c == '\n' || c == '\t' || c == '\r') {   // 换行等字符可以
                continue;
            }

            if (c > 127 || c < 32) {   // 其它不可见字符不行
                return false;
            }
        }

        return true;
    }

    private static boolean isGB2312(byte[] data) {
        // gb2312 高位 0xA1-0xFE
        if (data.length > 0) {
            if (data[0] >= 0xa1 && data[0] <= 0xfe) {
                return true;
            }
        }
        return false;
    }

    private String getStringFromByte(byte[] data, String version) {
        // 从前到后找第5个豆号
        try {
            int iStart = getIndex(data, (byte) 0x2C, 5) + 1;
            if (iStart > 0) {
                // 提取短信内容部分
                byte[] bySrc = new byte[data.length - iStart];
                System.arraycopy(data, iStart, bySrc, 0, data.length - iStart);

                // 看看有多少个5D
                int i5D = 0;
                for (int i = 0; i < bySrc.length - 2; i++) {
                    if (bySrc[i] == 0x89 && bySrc[i + 1] == 0x9A && bySrc[i + 2] == 0xAE) {
                        i5D++;
                    }
                }

                // 根据5D的个数，算出总长度
                byte[] byDes = new byte[bySrc.length - 2 * i5D];
                int iDes = 0;

                // 转译回其中包含']',即0x5D
                for (int i = 0; i < bySrc.length; i++) {
                    if (i < bySrc.length - 2 && bySrc[i] == 0x89 && bySrc[i + 1] == 0x9A && bySrc[i + 2] == 0xAE) {
                        byDes[iDes++] = 0x5D;
                        i += 2;
                    } else {
                        byDes[iDes++] = bySrc[i];
                    }
                }

                // 如果是国际版本，并且不全是ASCII字符，就要用UNICODE转换
                String charsetName;
                if (version.startsWith("W")) {
                    if (isAscii(byDes)) {
                        charsetName = CharsetName.US_ASCII;
                    } else {
                        charsetName = CharsetName.UTF_16LE;
                    }
                } else {
                    if (isAscii(byDes)) {
                        charsetName = CharsetName.US_ASCII;
                    } else if (isGB2312(byDes)) {
                        charsetName = CharsetName.GBK;
                    } else {
                        charsetName = CharsetName.UTF_16LE;
                    }
                }
                return new String(byDes, 0, byDes.length, charsetName);
            }
        } catch (Exception e) {
            logger.error("getStringFromByte", e);
        }

        return "";
    }

    private String getStatusString(String mno, String status) {
        String strRet = "";
        try {
            // 终端传过来的是个字符串，解析为字符
            int iStatus = Integer.parseInt(status);
            // 先看看是否休眠,D8位为1表示休眠
            if ((iStatus & (1 << 8)) != 0) {
                strRet = String.format("%s%d:", strRet, TextTerDef.STATUS_SLEEP);
            }
            // 再看看是否断电,低8位为0xFF表断电，断电了就不解析其它位了
            if ((iStatus & 0xff) == 0xff) {
                strRet = String.format("%s%d:", strRet, TextTerDef.STATUS_POWEROFF);
            } else {
                // 根据设备编号最高位，0开头为电动车
                if (mno.startsWith("0")) {
                    // D5是ACC
                    if ((iStatus & (1 << 5)) != 0) {
                        strRet = String.format("%s%d:", strRet, TextTerDef.STATUS_ACCON);
                    }
                    // D3位表示电门通断
                    if ((iStatus & (1 << 3)) != 0) {
                        strRet = String.format("%s%d:", strRet, TextTerDef.STATUS_SWON);
                    }
                    // D4位表示电机锁状态
                    if ((iStatus & (1 << 4)) != 0) {
                        strRet = String.format("%s%d:", strRet, TextTerDef.STATUS_EMLOCK);
                    }
                    // D0 D1位表示一个数值，为了兼容早期设备不得已而为之
                    int iLow = iStatus & 0x3;
                    // 加锁
                    if (iLow == 0x01) {
                        strRet = String.format("%s%d:", strRet, TextTerDef.STATUS_LOCK);
                    } // 解锁
                    else if (iLow == 0x02) {
                        strRet = String.format("%s%d:", strRet, TextTerDef.STATUS_UNLOCK);
                    } // 运动
                    else if (iLow == 0x03) {
                        strRet = String.format("%s%d:", strRet, TextTerDef.STATUS_MOVE);
                    }
                }
                // 如果是汽车
                else {
                    // D4是门磁状态
                    if ((iStatus & (1 << 4)) != 0) {
                        strRet = String.format("%s%d:", strRet, TextTerDef.STATUS_MAGNETIC_ON);
                    }
                    // D3是ACC
                    if ((iStatus & (1 << 3)) != 0) {
                        strRet = String.format("%s%d:", strRet, TextTerDef.STATUS_ACCON);
                    }
                    // D2是门检测
                    if ((iStatus & (1 << 2)) != 0) {
                        strRet = String.format("%s%d:", strRet, TextTerDef.STATUS_DOORON);
                    }
                    // D1是油门断
                    if ((iStatus & (1 << 1)) != 0) {
                        strRet = String.format("%s%d:", strRet, TextTerDef.STATUS_OILOFF);
                    }
                    // D0是加锁解锁
                    if ((iStatus & (1 << 0)) != 0) {
                        strRet = String.format("%s%d:", strRet, TextTerDef.STATUS_LOCK);
                    } else {
                        strRet = String.format("%s%d:", strRet, TextTerDef.STATUS_UNLOCK);
                    }
                }
                // D9 位表示低电休眠
                if ((iStatus & (1 << 9)) != 0) {
                    strRet = String.format("%s%d:", strRet, TextTerDef.STATUS_LOW_POWER_SLEEP);
                }
            }
        } catch (Exception e) {
            logger.error("getStatusString", e);
        }
        return strRet;
    }

    private ATerPkg analyzeTerPos(String[] ss) {
        return analyzeTerPos(ss, "", "");
    }

    private ATerPkg analyzeTerPos(String[] ss, String rspCmd) {
        return analyzeTerPos(ss, rspCmd, "");
    }

    private ATerPkg analyzeTerPos(String[] ss, String rspCmd, String alarm) {
        TextPos pkg = new TextPos();
        pkg.pointDt = getPkgDt(ss[INDEX_DT]);
        pkg.rspCmd = rspCmd;
        pkg.alarm = alarm;
        pkg.pointType = Integer.parseInt(ss[5]);
        // 如查未定位，则不用解析纬度了
        if (pkg.pointType == 0) {
            pkg.lng = 0;
            pkg.lat = 0;
            pkg.speed = 0;
            pkg.direction = 0;
        } else {
            // 解析经度
            pkg.lng = Double.parseDouble(ss[7]);
            if (ss[6] == "W") {
                pkg.lng = 0 - pkg.lng;
            }
            // 解析纬度
            pkg.lat = Double.parseDouble(ss[9]);
            if (ss[8] == "S") {
                pkg.lat = 0 - pkg.lat;
            }
            // 解析速度,将海里转换为公里
            if (ss.length > 10 && !Objects.equals(ss[10], "")) {
                pkg.speed = (int) Math.round(Double.parseDouble(ss[10]) * 1.852);
            } else {
                pkg.speed = 0;
            }

            // 解析方向
            if (ss.length > 11 && !Objects.equals(ss[11], "")) {
                pkg.direction = (int) Math.round(Double.parseDouble(ss[11]));
            } else {
                pkg.direction = 0;
            }
        }

        //解析状态
        pkg.status = getStatusString(ss[3], ss[12]);

        //获取CELLID
        if (ss.length > 13) {
            pkg.cellId = ss[13];
        }
        // 如果有信号强度
        if (ss.length > 14) {
            pkg.ggp = ss[14];
        }

        return pkg;
    }

    private ATerPkg analyzeLogin(String[] ss) {
        TextTerLogin pkg = new TextTerLogin();
        pkg.simNO = ss[5];                   // SIM卡号
        pkg.ownerMobile = ss[6];             // 车主号
        pkg.pwd = ss[7];                     // 密码.
        if (ss.length > 8) {
            pkg.loginReason = ss[8];             // 重新登录原因
        }
        if (ss.length > 9) {
            pkg.imsi = ss[9];               // IMSI
        }
        if (ss.length > 10) {
            pkg.imei = ss[10];              // IMEI
        }
        if (ss.length > 11) {
            pkg.manufacture = ss[11];        // 生产厂家
        }
        return pkg;
    }

    private ATerPkg analyzeHeart(String[] ss) {
        TextHeart pkg = new TextHeart();
        if (ss.length > 5) {    // 心跳包有可能带有位置
            pkg.ggp = ss[5];
        }
        //电源电压
        if (ss.length > 6) {
            pkg.v = ss[6];
        }
        return pkg;
    }

    private ATerPkg analyzeControlRsp(String[] ss, String controlName, String rspContent) {
        TextControlRsp pkg = new TextControlRsp();
        pkg.controlName = controlName;
        pkg.rspContent = rspContent;
        return pkg;
    }

    private ATerPkg analyzeCommPkg(String[] ss, String rspCmd) {
        TextCommPkg pkg = new TextCommPkg();
        pkg.rspCmd = rspCmd;
        return pkg;
    }

    @Override
    public ByteBuf builtRemoteControlPkg(AConnInfo conn, ABaseHandleObj aBaseHandleObj) {
        ByteBuf byteBuf = null;
        RemoteControl remoteControl = (RemoteControl) aBaseHandleObj;
        try {
            String serNO = null;
            if (ConstDefine.CONTROL_TYPE_GET.equals(remoteControl.controlType)) {
                String paramName = remoteControl.paramName.replace(";", "");
                serNO = String.format("%s.%s", remoteControl.sn, TextTerDef.CONTROL_CXCS);
                // 流水号存在直接返回
                if (TerGatewayData.serNoCheck(serNO)) {
                    return null;
                }
                String pkgStr = String.format("[,S14,%s]", paramName);
                byteBuf = buildPkg(pkgStr);
            } else if (ConstDefine.CONTROL_TYPE_SET.equals(remoteControl.controlType)) {
                // 去掉分号
                remoteControl.paramName = remoteControl.paramName.replace(";", "");
                String[] params = remoteControl.paramName.split("=");
                if (params.length > 1) {
                    serNO = String.format("%s.%s", remoteControl.sn, TextTerDef.CONTROL_SZCS);
                    // 流水号存在直接返回
                    if (TerGatewayData.serNoCheck(serNO)) {
                        return null;
                    }

                    String pkgStr = String.format("[,S2,%s=%s]", params[0], params[1]);
                    byteBuf = buildPkg(pkgStr);
                } else {
                    return null;
                }
            } else if (ConstDefine.CONTROL_TYPE_CONTROL.equals(remoteControl.controlType)) {
                String pkgStr = null;
                // 只支持部分
                Integer code = Integer.parseInt(remoteControl.paramName);
                // 部分设备没有应答，不需要流水号
                boolean bNeedSerNo = true;
                // 设防
                if (code == 0x1) {
                    serNO = String.format("%s.%s", remoteControl.sn, TextTerDef.CONTROL_SF);
                    pkgStr = "[,S12]";
                } else if (code == 0x02) {  // 撤防
                    serNO = String.format("%s.%s", remoteControl.sn, TextTerDef.CONTROL_CF);
                    pkgStr = "[,S13]";
                } else if (code == 0x03) {  // 重启
                    serNO = String.format("%s.%s", remoteControl.sn, TextTerDef.CONTROL_CQ);
                    pkgStr = "[,S11]";
                    bNeedSerNo = false;
                } else if (code == 0x0f) {  // 立即定位
                    serNO = String.format("%s.%s", remoteControl.sn, TextTerDef.CONTROL_DW);
                    pkgStr = "[,S10]";
                    bNeedSerNo = false;
                }
                if (null != pkgStr) {
                    // 流水号存在直接返回
                    if (bNeedSerNo && TerGatewayData.serNoCheck(serNO)) {
                        return null;
                    }
                    byteBuf = buildPkg(pkgStr);
                } else {
                    return null;
                }
            } else if (ConstDefine.CONTROL_TYPE_VOICE.equals(remoteControl.controlType)) {
                return null;
            } else {
                logger.error(String.format("构造下行数据包异常,未知的指令类型:[%s]", remoteControl.controlType));
                return null;
            }
            // 创建完成 写入全局缓存
            if (null != byteBuf) {
                TerGatewayData.setStrSerNoMap(serNO, remoteControl);
                return byteBuf;
            }
        } catch (Exception e) {
            if (byteBuf != null) {
                byteBuf.clear();
            }
            logger.error("构造下行数据包异常", e);
        }
        return null;
    }

    @Override
    public String getProtocolName() {
        return protocolName;
    }

    public ByteBuf buildPkg(String pkgStr) {
        ByteBuf rsp = PooledByteBufAllocator.DEFAULT.directBuffer();
        try {
            byte[] bs = pkgStr.getBytes(CharsetName.US_ASCII);
            rsp.writeBytes(bs);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return rsp;
    }

    public ByteBuf buildCommRsp(String rspCmd) {
        String pkgStr = String.format("[,%s]", rspCmd);
        return buildPkg(pkgStr);
    }
}
